package kieper.rpcgen;

import xsd.kieper.MessageSet;
import xsd.kieper.MessageSetDocument;

import java.io.*;
import java.util.List;
import java.util.ArrayList;

import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlValidationError;
import org.apache.log4j.Logger;

public class XmlParser {

    private static final Logger logger = Logger.getLogger(XmlParser.class);

    /**
     * Parse the input file and create the root element
     * @param path the path of the file to read the document from
     * @return the root element of the parsed document
     */
    public static MessageSet parse(String path) {
        File file = new File(path);
        return parse(file);
    }

    /**
     * Parse the input file and create the root element
     * @param file the file to read the document from
     * @return the root element of the parsed document
     */
    public static MessageSet parse(File file) {
        InputStream is = null;
        try {
            is = new FileInputStream(file);
            return parse(is);
        } catch (FileNotFoundException e) {
            throw new ParseException("Unable to open input file", e);
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                logger.warn("Could not close input stream", e);
            }
        }
    }

    /**
     * Parse the input file and create the root element
     * @param is the input stream to read the document from
     * @return the root element of the parsed document
     */
    public static MessageSet parse(InputStream is) {
        MessageSetDocument msd;
        try {
            msd = MessageSetDocument.Factory.parse(is);
        } catch (XmlException e) {
            throw new ParseException("Unable to parse input file", e);
        } catch (IOException e) {
            throw new ParseException("Unable to read from input file", e);
        }
        
        List<ValidationError> validationErrors =  validateDocument(msd);
        if (validationErrors == null) {
            return msd.getMessageSet();
        } else {
            throw new ParseException("Unable to validate input file against schema.", 
                    validationErrors);
        }
    }

    /**
     * Validate the parsed document against xml schema
     * @param msd the document to validate
     * @return null if document is valid, list of error messages otherwise
     */
    private static List<ValidationError> validateDocument(MessageSetDocument msd) {
        ArrayList<XmlValidationError> xbValidationErrors = new ArrayList<XmlValidationError>();
        XmlOptions validationOptions = new XmlOptions();
        validationOptions.setErrorListener(xbValidationErrors);
        boolean isValid = msd.validate(validationOptions);
        if (isValid) {
            return null;
        } else {
            List<ValidationError> validationErrors =
                    new ArrayList<ValidationError>(xbValidationErrors.size());
            for (XmlValidationError error : xbValidationErrors) {
                validationErrors.add(new ValidationError(error.getMessage(), error.getLine()));
            }
            return validationErrors;
        }
    }
}
